# 获取脚本编辑器引用 - 修改自"张学徒"的类库
# 可以用于插件或EditorScript中获取脚本编辑器，并进行相应操作
class_name ScriptEditorGetter

# 私有变量
var editor_plugin:EditorPlugin setget set_editor_plugin # 插件
var editor_interface:EditorInterface  setget set_editor_interface # EditorInterface - 编辑器界面
var script_editor:ScriptEditor #EditorInterface.ScriptEditor - 

var tab_container:TabContainer setget ,get_tab_container # 
var fileList:ItemList setget ,get_fileList # 左上方文件列表



var _file = File.new()


# ====== 初始化 ====== 
func _init(EditorFace:EditorInterface):
	editor_interface = EditorFace # 存储传入的EditorInterface实例
	script_editor = editor_interface.get_script_editor() # EditorInterface.ScriptEditor
	tab_container = get_tab_container()
	fileList = get_fileList()
	pass



# ====== setter\getter ====== 
func set_editor_plugin(value: EditorPlugin) -> void:
	editor_interface = value.get_editor_interface() # 返回并存储新的editor_interface
	
func set_editor_interface(value: EditorInterface) -> void:
	script_editor = value.get_script_editor() # 返回并存储新的script_editor


# 返回脚本编辑器页面的父节点 - TabContainer
# 返回TabContainer节点，里面包含ScriptEditor列表
func get_tab_container() -> TabContainer:
	var m_tab_container = script_editor.get_child(0).get_child(1).get_child(1) as TabContainer
	return m_tab_container

# 返回脚本编辑器左侧上方文件列表
func get_fileList() -> ItemList:
	var m_fileList =  tab_container.get_parent().get_child(0).get_child(0).get_child(1) as ItemList
	return m_fileList

## 返回当前代码编辑框
func get_current_code_editor() -> TextEdit:
	return _get_code_editor(tab_container.get_current_tab_control()) 


##  获取当前脚本
## @return  返回当前脚本
func get_current_script() -> Script:
	return script_editor.get_current_script()

##  返回脚本编辑框列表
func get_script_editor_list() -> Array:
	var list := []
	for child in get_tab_container().get_children():
		if child.get_class() == "ScriptTextEditor":
			list.push_back(child)
	return list

##  返回脚本编辑器菜单的父节点
func get_script_editor_menu_parent() -> HBoxContainer:
	# 获取编辑器视图
	var v_editor = editor_interface.get_editor_viewport()
	# 获取脚本编辑器菜单列表
	return v_editor \
			.get_child(2) \
			.get_child(0) \
			.get_child(0)


##  返回脚本编辑框下的代码编辑框 
## @script_editor  脚本编辑框
## @return  返回代码编辑框 TextEdit
## （脚本编辑框和代码编辑框的区别：
##  * 脚本编辑框是个各种节点组合成的复合节点
##  * 代码编辑框是个文本编辑框，可直接输入操控代码
## ）
func _get_code_editor(scripteditor: Control) -> TextEdit:
	var code_editor = scripteditor
	# 代码文本编辑器在第3级子节点
	for i in 3:
		if code_editor == null:
			return null
		code_editor = code_editor.get_child(0)
	return code_editor








#==================================================
#   获取脚本相关数据
#==================================================
##  获取已打开的脚本路径列表
## @return  返回脚本路径列表
func get_open_script_path_list() -> Array:
	var list := []
	var data_list := get_script_data()
	for data in data_list:
		list.push_back(data["script_path"])
	return list


##  获取脚本数据
## @return  返回代码编辑器左侧脚本列表信息
##   数据中的 key 包括：
##        item_name item名称
##        script_path 脚本路径、
##        tab_idx 脚本编辑框的 tab 索引
func get_script_data() -> Array:
	var fileList = get_fileList()
	var data_list = []
	var idx = 0
	for i in range(fileList.items.size() / 3):
		idx = 3 * i
		var path = fileList.get_item_tooltip(i)
		if path.right(path.length() - 3) == ".gd":
			data_list.push_back({
				item_name = fileList.items[idx],
				script_path = fileList.get_item_tooltip(i),
				tab_idx = fileList.get_item_metadata(i),
			})
	return data_list


##  获取指定脚本的脚本编辑框的 tab 值
## @script_path  脚本路径
## @return  返回 tab 值，如果没有则返回 -1
func _get_script_tab(script_path: String) -> int:
	var data_list := get_script_data()
	for data in data_list:
		if data["script_path"] == script_path:
			return data['tab_idx']
	return -1



#==================================================
#   执行功能动作
#==================================================
##  添加脚本编辑器菜单 
## @menu_button  菜单按钮
func add_script_editor_menu(menu_button: MenuButton) -> void:
	var parent = get_script_editor_menu_parent()
	var last_menu_idx : int = 0
	for idx in range(parent.get_children().size() -1, -1, -1):
		var child = parent.get_child(idx)
		if child is MenuButton && not child is ToolButton:
			last_menu_idx = child.get_index()
			break
	# 添加 MenuButton 节点
	parent.call_deferred("add_child", menu_button, true)
	parent.move_child(menu_button, last_menu_idx + 1)


##  切换到指定脚本的 tab_idx 页
## @script_path  脚本的路径
func switch_tab_by_script_path(script_path: String) -> void:
	var tab_idx = _get_script_tab(script_path)
	if tab_idx > -1:
		get_tab_container().current_tab = tab_idx
	else:
		# 切换失败没有脚本 就没有这个 tab
		push_warning("[Fail] Script does not exist.")

# 以索引切换脚本的页面
func switch_tab_by_index(index: int) -> void:
	get_tab_container().current_tab = index



##  关闭脚本
## @script  脚本或带有路径的脚本
func close_script(script) -> void:
	var script_path = ""
	if script is String: # 传入的是路径
		script_path = script
	elif script is Script: # 传入的是脚本对象
		script_path = script.resource_path
	elif script is int: # 传入索引
		var tab_idx = script
		close_script_by_tab(tab_idx)
		return
	else:
		printerr("[参数错误] 参数值必须是 <String/Script> 类型！")
		return
	
	var tab_idx = _get_script_tab(script_path)
	if tab_idx > -1:
		close_script_by_tab(tab_idx)
	else:
		# 关闭失败没有这个 tab
		push_warning("[Fail] Script does not exist.")


##  关闭脚本（通过 idx 关闭）
## @tab_idx  tab索引值
func close_script_by_tab(tab_idx: int) -> void:
	var fileList = get_fileList()
	var tab_container = get_tab_container()
	var data_list = get_script_data()
	if data_list.size() <= tab_idx:
		print("tab_idx 超出总数量")
		return
	
	# item 名称
	var item := data_list[tab_idx]['item_name'] as String
	
	# 如果是未保存的
	if item.right(item.length() - 3) == "(*)":
		print("[关闭脚本失败] 脚本还未保存!")
		push_warning("[Fail] The script has not been saved!")
		return
	# 移除
	fileList.remove_item(tab_idx)
	tab_container.get_child(tab_idx).queue_free()


##  脚本是否已保存
## @script_path  脚本路径
## @return  返回是否保存了
func script_is_saved(script_path: String) -> bool:
	var data_list = get_script_data()
	var idx = 0
	for data in data_list:
		if data["script_path"] == script_path:
			var item := data_list[idx]['item_name'] as String
			# item末尾字符串不是 (*) 则代表已保存
			return item.right(item.length() - 3) != "(*)"
		idx += 1
	return true


##  重新加载指定脚本的代码编辑框中的代码
## 需要重新保存
## @script_path  脚本路径
func reload_code_editor_code(script_path: String) -> void:
	# 判断是否存在文件
	
	if not _file.file_exists(script_path):
		printerr("[重新加载失败] 指定脚本不存在")
		return
	
	# 获取代码编辑框
	var tab_container = get_tab_container()
	var tab_idx = _get_script_tab(script_path)
	if tab_idx == -1:
		return
	var script_editor = tab_container.get_tab_control(tab_idx)
	var code_editor := _get_code_editor(script_editor) as TextEdit
	if code_editor == null:
		print("[重新加载失败] 没有 %s 文件的 CodeEditor" % script_path)
		return
	
	# 打开文件
	_file.open(script_path, File.READ)
	var code = _file.get_as_text()
	_file.close()
	
	# 更新代码编辑框的内容
	if code != code_editor.text:
		code_editor.text = code

